#include "jnihelper.h"
#include <ft2build.h>
#include <freetype/freetype.h>

JNIEXPORT jlong JNICALL Java_com_koc_robot_freetype_FreeType_initFreeTypeJni(JNIEnv* env, jclass clazz)
{
	FT_Library library = 0;
	FT_Error error = FT_Init_FreeType(&library);
	if (error) return 0;
	else return (jlong)library;
} 

JNIEXPORT void JNICALL Java_com_koc_robot_freetype_FreeType_doneFreeTypeJni(JNIEnv* env, jclass clazz, jlong library)
{
	FT_Done_FreeType((FT_Library)library);
}

static inline jlong wrapped_Java_com_koc_robot_freetype_FreeType_newMemoryFace
(JNIEnv* env, jclass clazz, jlong library, jobject obj_data, jint dataSize, jint faceIndex, char* data)
{
	FT_Face face = 0;
	FT_Error error = FT_New_Memory_Face((FT_Library)library, (const FT_Byte*)data, dataSize, faceIndex, &face);
	if (error) return 0;
	else return (jlong)face;
}

JNIEXPORT jlong JNICALL Java_com_koc_robot_freetype_FreeType_newMemoryFace(JNIEnv* env, jclass clazz, jlong library, jobject obj_data, jint dataSize, jint faceIndex)
{
	char* data = (char*)(*env)->GetDirectBufferAddress(env, obj_data);

	jlong JNI_returnValue = wrapped_Java_com_koc_robot_freetype_FreeType_newMemoryFace(env, clazz, library, obj_data, dataSize, faceIndex, data);

	return JNI_returnValue;
}

JNIEXPORT void JNICALL Java_com_koc_robot_freetype_FreeType_doneFace(JNIEnv* env, jclass clazz, jlong face)
{
    FT_Done_Face((FT_Face)face);
}

JNIEXPORT jboolean JNICALL Java_com_koc_robot_freetype_FreeType_setPixelSizes(JNIEnv* env, jclass clazz, jlong face, jint pixelWidth, jint pixelHeight)
{
	return !FT_Set_Pixel_Sizes((FT_Face)face, pixelWidth, pixelHeight);
}

JNIEXPORT jboolean JNICALL Java_com_koc_robot_freetype_FreeType_loadGlyph(JNIEnv* env, jclass clazz, jlong face, jint glyphIndex, jint loadFlags)
{
    return !FT_Load_Glyph((FT_Face)face, glyphIndex, loadFlags);
}


JNIEXPORT jboolean JNICALL Java_com_koc_robot_freetype_FreeType_renderGlyph(JNIEnv* env, jclass clazz, jlong slot, jint renderMode)
{
    return !FT_Render_Glyph((FT_GlyphSlot)slot, (FT_Render_Mode)renderMode);
}
 

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_FreeType_getCharIndex(JNIEnv* env, jclass clazz, jlong face, jint charCode)
{
    return FT_Get_Char_Index((FT_Face)face, charCode);
}

/* Bitmap */
JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_Bitmap_getRows(JNIEnv* env, jclass clazz, jlong bitmap)
{
	return ((FT_Bitmap*)bitmap)->rows;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_Bitmap_getWidth(JNIEnv* env, jclass clazz, jlong bitmap)
{
	return ((FT_Bitmap*)bitmap)->width;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_Bitmap_getPitch(JNIEnv* env, jclass clazz, jlong bitmap)
{
	return ((FT_Bitmap*)bitmap)->pitch;
}

JNIEXPORT jobject JNICALL Java_com_koc_robot_freetype_Bitmap_getBuffer(JNIEnv* env, jclass clazz, jlong bitmap)
{
	FT_Bitmap* bmp = (FT_Bitmap*)bitmap;
	return (*env)->NewDirectByteBuffer(env, (void*)bmp->buffer, bmp->rows * abs(bmp->pitch));
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_Bitmap_getNumGray(JNIEnv* env, jclass clazz, jlong bitmap)
{
	return ((FT_Bitmap*)bitmap)->num_grays;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_Bitmap_getPixelMode(JNIEnv* env, jclass clazz, jlong bitmap)
{
	return ((FT_Bitmap*)bitmap)->pixel_mode;
}

/* GlyphMetrics */
JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphMetrics_getWidth(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Glyph_Metrics*)metrics)->width;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphMetrics_getHeight(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Glyph_Metrics*)metrics)->height;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphMetrics_getHoriBearingX(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Glyph_Metrics*)metrics)->horiBearingX;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphMetrics_getHoriBearingY(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Glyph_Metrics*)metrics)->horiBearingY;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphMetrics_getHoriAdvance(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Glyph_Metrics*)metrics)->horiAdvance;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphMetrics_getVertBearingX(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Glyph_Metrics*)metrics)->vertBearingX;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphMetrics_getVertBearingY(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Glyph_Metrics*)metrics)->vertBearingY;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphMetrics_getVertAdvance(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Glyph_Metrics*)metrics)->vertAdvance;
}

/* face */
JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_Face_getFaceFlags(JNIEnv* env, jclass clazz, jlong face)
{
	return ((FT_Face)face)->face_flags;
}

JNIEXPORT jlong JNICALL Java_com_koc_robot_freetype_Face_getGlyph(JNIEnv* env, jclass clazz, jlong face)
{
    return (jlong)((FT_Face)face)->glyph;
}

JNIEXPORT jlong JNICALL Java_com_koc_robot_freetype_Face_getSize(JNIEnv* env, jclass clazz, jlong face)
{
    return (jlong)((FT_Face)face)->size;
}

/* size */
JNIEXPORT jlong JNICALL Java_com_koc_robot_freetype_Size_getMetrics(JNIEnv* env, jclass clazz, jlong address)
{
    return (jlong)&((FT_Size)address)->metrics;
}

/* size metrics */
JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_SizeMetrics_getXppem(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Size_Metrics*)metrics)->x_ppem;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_SizeMetrics_getYppem(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Size_Metrics*)metrics)->y_ppem;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_SizeMetrics_getXscale(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Size_Metrics*)metrics)->x_scale;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_SizeMetrics_getYscale(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Size_Metrics*)metrics)->y_scale;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_SizeMetrics_getAscenter(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Size_Metrics*)metrics)->ascender;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_SizeMetrics_getDescender(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Size_Metrics*)metrics)->descender;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_SizeMetrics_getHeight(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Size_Metrics*)metrics)->height;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_SizeMetrics_getMaxAdvance(JNIEnv* env, jclass clazz, jlong metrics)
{
	return ((FT_Size_Metrics*)metrics)->max_advance;
}

/* GlyphSlot */
JNIEXPORT jlong JNICALL Java_com_koc_robot_freetype_GlyphSlot_getMetrics(JNIEnv* env, jclass clazz, jlong slot)
{
	return (jlong)&((FT_GlyphSlot)slot)->metrics;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphSlot_getLinearHoriAdvance(JNIEnv* env, jclass clazz, jlong slot)
{
	return ((FT_GlyphSlot)slot)->linearHoriAdvance;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphSlot_getLinearVertAdvance(JNIEnv* env, jclass clazz, jlong slot)
{
	return ((FT_GlyphSlot)slot)->linearVertAdvance;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphSlot_getAdvanceX(JNIEnv* env, jclass clazz, jlong slot)
{
	return ((FT_GlyphSlot)slot)->advance.x;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphSlot_getAdvanceY(JNIEnv* env, jclass clazz, jlong slot)
{
	return ((FT_GlyphSlot)slot)->advance.y;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphSlot_getFormat(JNIEnv* env, jclass clazz, jlong slot)
{
	return ((FT_GlyphSlot)slot)->format;
}

JNIEXPORT jlong JNICALL Java_com_koc_robot_freetype_GlyphSlot_getBitmap(JNIEnv* env, jclass clazz, jlong slot)
{
	FT_GlyphSlot glyph = ((FT_GlyphSlot)slot);
	return (jlong)&(glyph->bitmap);
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphSlot_getBitmapLeft(JNIEnv* env, jclass clazz, jlong slot)
{
	return ((FT_GlyphSlot)slot)->bitmap_left;
}

JNIEXPORT jint JNICALL Java_com_koc_robot_freetype_GlyphSlot_getBitmapTop(JNIEnv* env, jclass clazz, jlong slot)
{
	return ((FT_GlyphSlot)slot)->bitmap_top;
}

JNIEXPORT void JNICALL Java_com_koc_robot_util_BufferUtils_freeMemory(JNIEnv* env, jclass clazz, jobject obj_buffer)
{
    char* buffer = (char*)(*env)->GetDirectBufferAddress(env, obj_buffer);
    
    free(buffer);
}

JNIEXPORT jobject JNICALL Java_com_koc_robot_util_BufferUtils_newDisposableByteBuffer(JNIEnv* env, jclass clazz, jint numBytes)
{
    char* ptr = (char*)malloc(numBytes);
    return (*env)->NewDirectByteBuffer(env, ptr, numBytes);
}

JNIEXPORT void JNICALL Java_com_koc_robot_util_BufferUtils_copyJni(JNIEnv* env, jclass clazz, jbyteArray obj_src, jint srcOffset, jobject obj_dst, jint dstOffset, jint numBytes)
{
    unsigned char* dst = (unsigned char*)(*env)->GetDirectBufferAddress(env, obj_dst);
    char* src = (char*)(*env)->GetPrimitiveArrayCritical(env, obj_src, 0);
    
    memcpy(dst + dstOffset, src + srcOffset, numBytes);
    
    (*env)->ReleasePrimitiveArrayCritical(env, obj_src, src, 0);
}

